package com.jfinal.ext.ueditor.storage;

import com.jfinal.core.JFinal;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.NotNull;

import java.io.*;
import java.util.UUID;

/**
 * @author Timor
 * @version 1.0
 * @date 2023/03/15 19:40:27
 */
public class FileStorageManager implements IStorageManager {
    public static final int DEFAULT_BUFFER_SIZE = 8192;
    public static final String DEFAULT_URL_PREFIX = "/upload";
    private final int bufferSize;
    private final String rootPath;
    private final String urlPrefix;

    public FileStorageManager() {
        this(JFinal.me().getConstants().getBaseUploadPath(), DEFAULT_URL_PREFIX, DEFAULT_BUFFER_SIZE);
    }

    public FileStorageManager(@NotNull String rootPath) {
        this(rootPath, DEFAULT_URL_PREFIX, DEFAULT_BUFFER_SIZE);
    }

    public FileStorageManager(@NotNull String rootPath, @NotNull String urlPrefix) {
        this(rootPath, urlPrefix, DEFAULT_BUFFER_SIZE);
    }

    public FileStorageManager(@NotNull String rootPath, @NotNull String urlPrefix, int bufferSize) {
        this.rootPath = rootPath;
        this.urlPrefix = urlPrefix;
        this.bufferSize = bufferSize;
    }


    @Override
    public StorageResult saveFile(File tempFile, String relativePath) {
        final File file = new File(rootPath, relativePath);

        final StorageResult result = saveTmpFile(tempFile, file);

        if (result.isFail()) {
            FileUtils.deleteQuietly(tempFile);

            return StorageResult.fail("IO错误");
        }

        result.setRelativePath(relativePath);
        result.setUrl(toUrl(relativePath));
        return result;
    }

    @Override
    public StorageResult saveBinaryFile(byte[] data, String relativePath) {
        final File file = new File(rootPath, relativePath);

        final StorageResult validResult = valid(file);

        if (validResult.isFail()) {
            return validResult;
        }


        try (final FileOutputStream fos = new FileOutputStream(file);
             final BufferedOutputStream bos = new BufferedOutputStream(fos, bufferSize)) {
            bos.write(data);
            bos.flush();
        } catch (IOException e) {
            FileUtils.deleteQuietly(file);

            return StorageResult.fail("IO错误");
        }

        final StorageResult result = StorageResult.ok();
        result.setRelativePath(relativePath);
        result.setUrl(toUrl(relativePath));
        result.setLength(data.length);
        return result;
    }

    @Override
    public StorageResult saveFileByInputStream(InputStream input, long inputSize, String relativePath) {
        final File tmpFile = getTmpFile();

        try (final BufferedInputStream bis = new BufferedInputStream(input, bufferSize);
             final FileOutputStream fos = new FileOutputStream(tmpFile);
             final BufferedOutputStream bos = new BufferedOutputStream(fos, bufferSize)) {

            final byte[] dataBuf = new byte[2048];
            int count;

            while ((count = bis.read(dataBuf)) != -1) {
                bos.write(dataBuf, 0, count);
            }
            bos.flush();
        } catch (IOException e) {
            FileUtils.deleteQuietly(tmpFile);

            return StorageResult.fail("IO错误");
        }

        return saveFile(tmpFile, relativePath);
    }

    private File getTmpFile() {
        String tmpFileName = "/temp/fs_" + UUID.randomUUID() + ".tmp";
        return new File(rootPath, tmpFileName);
    }

    private static StorageResult saveTmpFile(File tmpFile, File targetFile) {
        if (targetFile.canWrite()) {
            return StorageResult.fail("创建文件失败");
        }

        try {
            FileUtils.moveFile(tmpFile, targetFile);
        } catch (IOException e) {
            return StorageResult.fail("IO错误");
        }


        final StorageResult result = StorageResult.ok();
        result.setLength(tmpFile.length());
        return result;
    }

    private static StorageResult valid(File file) {
        File parentPath = file.getParentFile();

        if ((!parentPath.exists()) && (!parentPath.mkdirs())) {
            return StorageResult.fail("创建文件失败");
        }

        if (!parentPath.canWrite()) {
            return StorageResult.fail("权限不足");
        }

        return StorageResult.ok();
    }

    protected String toUrl(String relativePath) {
        return urlPrefix + relativePath;
    }

}